from binn import *

lib.binn_list.restype = ct.c_voidp


def binn_list():
    binn = lib.binn_list()
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return binn


lib.binn_list_add_bool.argtypes = [ct.c_voidp, ct.c_bool]


def binn_list_add_bool(binn, val):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_add_bool(binn, val)


lib.binn_list_add_bool.argtypes = [ct.c_voidp, ct.c_bool]


def binn_list_add_bool(binn, val):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_add_bool(binn, val)


lib.binn_list_uint8.argtypes = [ct.c_voidp, ct.c_int]
lib.binn_list_uint8.restype = ct.c_uint8


def binn_list_uint8(binn, iden):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_uint8(binn, iden)


lib.binn_list_add_uint8.argtypes = [ct.c_voidp, ct.c_uint8]


def binn_list_add_uint8(binn, val):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_add_uint8(binn, val)


lib.binn_list_add_uint8.argtypes = [ct.c_voidp, ct.c_uint8]


def binn_list_add_uint8(binn, val):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_add_uint8(binn, val)


lib.binn_list_uint8.argtypes = [ct.c_voidp, ct.c_int]
lib.binn_list_uint8.restype = ct.c_uint8


def binn_list_uint8(binn, iden):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_uint8(binn, iden)


lib.binn_list_add_uint16.argtypes = [ct.c_voidp, ct.c_uint16]


def binn_list_add_uint16(binn, val):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_add_uint16(binn, val)


lib.binn_list_uint16.argtypes = [ct.c_voidp, ct.c_int]
lib.binn_list_uint16.restype = ct.c_uint16


def binn_list_uint16(binn, iden):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_uint16(binn, iden)


lib.binn_list_add_uint32.argtypes = [ct.c_voidp, ct.c_uint32]


def binn_list_add_uint32(binn, val):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_add_uint32(binn, val)


lib.binn_list_uint32.argtypes = [ct.c_voidp, ct.c_int]
lib.binn_list_uint32.restype = ct.c_uint32


def binn_list_uint32(binn, iden):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_uint32(binn, iden)


lib.binn_list_add_uint64.argtypes = [ct.c_voidp, ct.c_uint64]


def binn_list_add_uint64(binn, val):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_add_uint64(binn, val)


lib.binn_list_uint64.argtypes = [ct.c_voidp, ct.c_int]
lib.binn_list_uint64.restype = ct.c_uint64


def binn_list_uint64(binn, iden):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_uint64(binn, iden)


lib.binn_list_int8.argtypes = [ct.c_voidp, ct.c_int]
lib.binn_list_int8.restype = ct.c_int8


def binn_list_int8(binn, iden):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_int8(binn, iden)


lib.binn_list_add_int16.argtypes = [ct.c_voidp, ct.c_int16]


def binn_list_add_int16(binn, val):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_add_int16(binn, val)


lib.binn_list_int16.argtypes = [ct.c_voidp, ct.c_int]
lib.binn_list_int16.restype = ct.c_int16


def binn_list_int16(binn, iden):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_int16(binn, iden)


lib.binn_list_add_int32.argtypes = [ct.c_voidp, ct.c_int32]


def binn_list_add_int32(binn, val):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_add_int32(binn, val)


lib.binn_list_int32.argtypes = [ct.c_voidp, ct.c_int]
lib.binn_list_int32.restype = ct.c_int32


def binn_list_int32(binn, iden):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_int32(binn, iden)


lib.binn_list_add_int64.argtypes = [ct.c_voidp, ct.c_int64]


def binn_list_add_int64(binn, val):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_add_int64(binn, val)


lib.binn_list_int64.argtypes = [ct.c_voidp, ct.c_int]
lib.binn_list_int64.restype = ct.c_int64


def binn_list_int64(binn, iden):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_int64(binn, iden)


lib.binn_list_add_float.argtypes = [ct.c_voidp, ct.c_float]


def binn_list_add_float(binn, val):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_add_float(binn, val)


lib.binn_list_float.argtypes = [ct.c_voidp, ct.c_int]
lib.binn_list_float.restype = ct.c_float


def binn_list_float(binn, iden):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_float(binn, iden)


lib.binn_list_add_double.argtypes = [ct.c_voidp, ct.c_double]


def binn_list_add_double(binn, val):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_add_double(binn, val)


lib.binn_list_double.argtypes = [ct.c_voidp, ct.c_int]
lib.binn_list_double.restype = ct.c_double


def binn_list_double(binn, iden):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_double(binn, iden)


lib.binn_list_add_str.argtypes = [ct.c_voidp, ct.c_char_p, ct.c_int]


def binn_list_add_str(binn, val):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_add_str(binn, val)


lib.binn_list_str.argtypes = [ct.c_voidp, ct.c_int]
lib.binn_list_str.restype = ct.c_char_p


def binn_list_str(binn, iden):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_str(binn, iden)


lib.binn_list_add_blob.argtypes = [ct.c_voidp, ct.c_voidp, ct.c_int]


def binn_list_add_blob(binn, iden, val, size):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_add_blob(binn, iden, val, size)


lib.binn_list_blob.argtypes = [ct.c_voidp, ct.POINTER(ct.c_int)]
lib.binn_list_blob.restype = ct.c_voidp


def binn_list_blob(binn, iden):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    size = ct.c_int(0)
    blob = lib.binn_list_blob(binn, iden, ct.byref(size))
    if not isinstance(blob, ct.c_voidp):
        blob = ct.c_voidp(blob)
    return blob, size.value


lib.binn_list_add_object.argtypes = [ct.c_voidp, ct.c_voidp]


def binn_list_add_object(binn, key, val):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_add_obect(binn, key, val)


lib.binn_list_object.argtypes = [ct.c_voidp, ct.c_int]
lib.binn_list_object.restype = ct.c_voidp


lib.binn_list_add_map.argtypes = [ct.c_voidp, ct.c_voidp]


def binn_list_add_map(binn, val):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_add_map(binn, val)


lib.binn_list_map.argtypes = [ct.c_voidp, ct.c_int]
lib.binn_list_map.restype = ct.c_voidp


def binn_list_map(binn, key):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_map(binn, key)

def binn_list_list(binn, key):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_list(binn, key)

lib.binn_list_add_list.argtypes = [ct.c_voidp, ct.c_voidp]


def binn_list_add_list(binn, val):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_add_list(binn, val)


lib.binn_list_list.argtypes = [ct.c_voidp, ct.c_int]
lib.binn_list_list.restype = ct.c_voidp


def binn_list_list(binn, key):
    if not isinstance(binn, ct.c_voidp):
        binn = ct.c_voidp(binn)
    return lib.binn_list_list(binn, key)



class BinnList():

    def __init__(self, *args):
        if len(args) == 1:
            self._binn = args[0]
            self._own = False
        else:
            self._binn = binn_list()
            self._own = True
    def __del__(self):
        if self._own:
            binn_free(self._binn)

    def begin(self):
        from binn_iter import BinnIter, binn_iter_init
        binn_iter = BinnIter(BINN_LIST)
        binn_iter_init(binn_iter.iter, self.binn, BINN_LIST)
        return binn_iter

    @property
    def binn(self):
        return self._binn

    def add_uint8(self, val):
        return binn_list_add_uint8(self._binn, val)

    def get_uint8(self, iden):
        return binn_list_uint32(self._binn, iden)

    def add_uint16(self, val):
        return binn_list_add_uint32(self._binn, val)

    def get_uint16(self, iden):
        return binn_list_uint32(self._binn, iden)

    def add_uint32(self, val):
        return binn_list_add_uint32(self._binn, val)

    def get_uint32(self, iden):
        return binn_list_uint32(self._binn, iden)

    def add_uint64(self, val):
        return binn_list_add_uint64(self._binn, val)

    def get_uint64(self, iden):
        return binn_list_uint64(self._binn, iden)

    def add_int8(self, val):
        return binn_list_add_int8(self._binn, val)

    def get_int8(self, iden):
        return binn_list_int32(self._binn, iden)

    def add_int16(self, val):
        return binn_list_add_int32(self._binn, val)

    def get_int16(self, iden):
        return binn_list_int32(self._binn, iden)

    def add_int32(self, val):
        return binn_list_add_int32(self._binn, val)

    def get_int32(self, iden):
        return binn_list_int32(self._binn, iden)

    def add_int64(self, val):
        return binn_list_add_int64(self._binn, val)

    def get_int64(self, iden):
        return binn_list_int64(self._binn, iden)

    def add_float(self, val):
        return binn_list_add_float(self._binn, val)

    def get_float(self, iden):
        return binn_list_float(self._binn, iden)

    def add_double(self, val):
        return binn_list_add_double(self._binn, val)

    def get_double(self, iden):
        return binn_list_double(self._binn, iden)

    def add_str(self, val):
        if isinstance(val, str):
            val = val.encode('utf-8')
        return binn_list_add_str(self._binn, val)

    def get_str(self, iden):
        return binn_list_str(self._binn, iden).decode()

    def add_ndarr(self, val):
        arr_as_voidp = val.ctypes.data_as(ct.c_voidp)
        size = val.size * val.itemsize
        return binn_list_add_blob(self._binn, iden, arr_as_voidp, size)

    def get_ndarr(self, iden, typ):
        ptr, size = binn_list_blob(self._binn, iden)
        if typ == 'f32':
            nitems = size // ct.sizeof(ct.c_float)
            buf = (ct.c_float * nitems)(10)
            ct.memmove(buf, ptr, size)
            return np.frombuffer(buf, np.float32)
        return

    def add_obj(self, binn_obj):
        return binn_list_add_object(self._binn, iden, binn_obj._binn)

    def get_obj(self, iden):
        from binn_obj import BinnObj
        binn_obj = binn_list_object(self._binn, iden)
        return BinnObj(binn_obj)

    def add_map(self, binn_map):
        return binn_list_add_map(self._binn, iden, binn_map._binn)

    def get_map(self, iden):
        from binn_map import BinnMap
        binn_map = binn_list_map(self._binn, iden)
        return BinnMap(binn_map)

    def add_list(self, binn_list):
        return binn_list_add_list(self._binn, binn_list._binn)

    def get_list(self, iden):
        binn_list = binn_list_list(self._binn, iden)
        return BinnList(binn_list)



